#!/bin/bash

#
# gpkg -- UI program for Xiange Linux package management system 
# 
# by Zhang Lihui <swordhuihui@gmail.com>, 2010-04-13, 15:58
#

#path define
ME="$(cd "${0%/*}" 2>/dev/null; echo "$PWD"/"${0##*/}")"
MEAT=$(dirname $ME)
XGPATH=$MEAT
XGPATH_TOOLS=$XGPATH/tools
XGPATH_LIB=$XGPATH/xglibs
XGPATH_SOURCE=$XGPATH/sources
XGPATH_PACKAGE=$XGPATH/packages
XGPATH_DB=$XGPATH/db
XGPATH_SCRIPT=$XGPATH_LIB

XGFILEINFO=$XGPATH/bin/xgfileinfo

XGPATH_UNPACK=/tmp/xiange/sources
XGPATH_DEST=/tmp/xiange/pack

#compile parameters
CC=gcc
CXX=g++
CFLAGS="-O2"
CXXFLAGS="$CFLAGS"
XGINST_PREFIX="--prefix=/usr"
XG_I18N="en zh_CN"

#GIT server
XG_GITSVR="git://github.com/swordhui/xglibs.git"

#color defines
## Set color commands, used via $ECHO
# Please consult `man console_codes for more information
# under the "ECMA-48 Set Graphics Rendition" section
#
# Warning: when switching from a 8bit to a 9bit font,
# the linux console will reinterpret the bold (1;) to
# the top 256 glyphs of the 9bit font. This does
# not affect framebuffer consoles
NORMAL="\\033[0;39m" # Standard console grey
SUCCESS="\\033[1;32m" # Success is green
WARNING="\\033[1;33m" # Warnings are yellow
FAILURE="\\033[1;31m" # Failures are red
INFO="\\033[1;36m" # Information is light cyan
BRACKET="\\033[1;34m" # Brackets are blue


#
# version and  usage 
#
gpkg_version=0.10.11.1
gpkg_usage="
gpkg $SUCCESS$gpkg_version$NORMAL, usage:\n
	$INFO\t-v            	$NORMAL\t\tshow current version\n
	$INFO\t--sync [path] 	$NORMAL\tsync xglibs via git to [path]/var/xiange\n
	$INFO\t-s package	$NORMAL\tsearch package\n
	$INFO\t-S file 		$NORMAL\tsearch which package file belongs to\n
	$INFO\t-d package 	$NORMAL\tdownload package\n
	$INFO\t-i package 	$NORMAL\tinstall package\n
	$INFO\t-D package 	$NORMAL\tremove package\n
	$INFO\t-I  		$NORMAL\t\tlist installed package\n
	$INFO\t-l package 	$NORMAL\tlist all files in installed package\n
	$INFO\t-ib xgp_file	$NORMAL\tinstall binary package to system\n
	$INFO\t-F path		$NORMAL\tfind orphans in path. e.g. not belongs to any package\n
	$INFO\t-chroot path	$NORMAL\tchroot to new system, for system installing\n
	$INFO\t-unchroot path	$NORMAL\tun-chroot\n
	$INFO\t-rdep package	$NORMAL\tshow reverse dependence\n
	$INFO\t-info		$NORMAL\t\tshow gpkg parameters. to modify parameters, gpkg info > /etc/xgparas, then edit /etc/xgparas\n
XianGe base is at $XGPATH\n
"

gpkg_show_usage()
{
	echo -e $gpkg_usage
}

#
# colourful echo 
#

#show info in cyan color, $1 is information.
showinfo()
{
	echo -e  $INFO"$1"$NORMAL
}

#show OK message in green color, $1 is information.
showOK()
{
	echo -e $SUCCESS"$1"$NORMAL
}

#show OK message in green color, $1 is information.
showFailed()
{
	echo -e $FAILURE"$1"$NORMAL
}

#
# string functions
#

#check: if $2 in $1, return 1, else return 0
str_isIn()
{
	if [ "${1/$2/}" != "${1}" ]; then
		return 1
	else
		return 0
	fi
}

#check: if $2 in $1 exactly. return 1 if in, return 0 if not in.
str_isInExactly()
{
	local str

	for str in $1
	do
		if [ "$str" == "$2" ]; then
			#in string.
			return 1
		fi
	done

	return 0
}

#
# Error check
#

#check return code. show $1 if error.
err_check()
{
	if [ $? != 0 ]; then
		echo -e $FAILURE"$1"
		exit 1
	fi
}


#
# version control
#

#parse atom to $N, $V, $R, 
#by tomgrean @ LinuxSir, 2009-09-11, 20:02
atom_parse()
{
	local ATOM
	local RR

	ATOM="${1%.xgb}"
	RR="${ATOM%-r[0-9]*}"
	R="${ATOM#$RR}"
	V="${RR##*-}"
	N="${RR%$V}"
	N="${N%-}"
}

#parse with type, such as "app-sys/vim-2.3.5"
#return type and name. 
atom_parse_type()
{
	N=${1##*/}
	T=${1%$N}
	T=${T%/}
}

#parse with path, suchas "/var/xiange/db/app-editor/vim/vim-7.2.401.xgb"
atom_parse_path()
{
	local atom

	atom=${1##*/}
	T=${1%/*}
	T=${T%/*}
	T=${T##*/}

	atom_parse "$atom"
}

#compare 2 versions, such 2.6.9 and 2.6.10
#return 0 if same. 
#return 1 if $1 is grater than $2 
#return 2 if $1 is lesser then $2
#by lastart @ LinuxSir
atom_vercmp()
{
	v1=$1.
	v2=$2.
	while true
	do
		a=${v1%%.*}
		b=${v2%%.*}
		[ -z "$a" ] && [ -z "$b" ] &&  return 0
		[ "$a"0 -gt "$b"0 ] && return 1
		[ "$a"0 -lt "$b"0 ] && return 2
		v1="${v1#*.}"
		v2="${v2#*.}"
	done
}

#compare 2 atoms, such as linux-2.6.9.xgb and linux-2.6.10.xgb
#return 0 if same. 
#return 1 if $1 is grater than $2 
#return 2 if $1 is lesser then $2
atom_cmp()
{
	local R1
	local V1
	local N1

	#echo "Begin Compare: $1 $2"

	atom_parse $1
	R1=$R
	V1=$V
	N1=$N
	#echo "N1=$N1, V1=$V1, R1=$R1"

	atom_parse $2
	#echo "N2=$N, V2=$V, R2=$R"

	#compare version
	atom_vercmp $V1 $V
}

#compare 2 atoms pth, such as /var/xiange/sys/linux-2.6.9.xgb and linux-2.6.10.xgb
#return 0 if same. 
#return 1 if $1 is grater than $2 
#return 2 if $1 is lesser then $2
atom_cmp_path()
{
	local R1
	local V1
	local N1

	#echo "Begin Compare: $1 $2"

	atom_parse_path $1
	R1=$R
	V1=$V
	N1=$N
	#echo "N1=$N1, V1=$V1, R1=$R1"

	atom_parse_path $2
	#echo "N2=$N, V2=$V, R2=$R"

	#compare version
	atom_vercmp $V1 $V
}
#
# CSV file operation
#

#split csv line. 
#input: $1="good,i love u,123"
#output: count=3, value[0]=good, value[1]="i love u", value[2]=123
csv_split()
{
	count=0
	local retv=$1
	unset value

	while [ -n "$retv" ]
	do
		value[$count]=${retv%%,*}
		#echo "value_$count=${value[$count]}"

		retv=${retv#${value[$count]}}
		retv=${retv#,}
		#echo "retv=$retv"

		count=$(($count+1))
	done
}

#
# search packages
#

#list all pack types
gpkg_get_types()
{
	gpkg_types=`ls $XGPATH_LIB 2>/dev/null`
}

gpkg_get_packages()
{
	gpkg_packs=`ls $XGPATH_LIB/$1 2>/dev/null`
}

#Get Newest file in dir $1
#return path of the newest on in $newest
gpkg_getnewest()
{
	local files
	local file
	newest=""

	files=`ls $1/*.xgb 2>/dev/null`
	for file in $files
	do
		if [ -z $newest ]; then
			newest=$file
			#echo "First is $file"
		else
			#compare 
			#echo "compare $file $newest"
			atom_cmp_path $file $newest
			[ "$?" == "1" ] && newest=$file
			#echo "after compare: new=$newest"

		fi
	done
}

#get all xgb versions, ret to $allver
#$1 is path 
gpkg_getallxgbver()
{
	local files
	local file
	allver=""

	files=`ls -t $1/*.xgb 2>/dev/null`
	for file in $files
	do
		atom_parse_path $file
		if [ -z "$allver" ]; then
			allver="$V$R"
		else
			allver+=" | $V$R"
		fi

		#include file for HOMEPAGE and DESCRIPTION
		. $file
	done
}

gpkg_searchInstalled()
{
	local searchpath=$XGPATH_DB/$1/$2	
	local findcnt=0

	local files=$(ls $searchpath/*.xgb 2>/dev/null)
	local file
	local -a value
	local t1
	local t2
	local tsize

	for file in $files
	do
		file=${file##*/}
		atom_parse $file

		#get date.
		csv_split `cat $searchpath/$N-$V$R.date`
		t1=${value[0]}
		t2=${value[1]}

		#get size, 
		csv_split `tail -n 1 $searchpath/$N-$V$R.file`
		tsize=${value[5]}

		printf "$NORMAL\tInstalled:$SUCCESS %s | %s %s $BRACKET%sK\n" $V$R $t1 $t2 $tsize 
		findcnt=$(($findcnt+1))
	done

	[ "$findcnt" == "0" ] && echo -e "$NORMAL\tInstalled:$FAILURE None"
}


#find one packge $1 is type, $2 is package name, $3 is index
gpkg_searchOK()
{
	local allver

	#show it.
	echo -e $FAILURE"* $1/$2"

	#get newest version
	gpkg_getallxgbver $XGPATH_LIB/$1/$2
	echo -e "$NORMAL\tAvailable:$INFO $allver"

	#search installed
	gpkg_searchInstalled $1 $2

	echo -e "$NORMAL\tDesc.    :$INFO $DESCRIPTION"
	echo -e "$NORMAL\tHomepage :$INFO $HOMEPAGE"

	#add blank line
	echo ""
}

#Search, $1 is atom with path, such as app-sys/linux
#or input linux only.
gpkg_search()
{
	local results
	local pack
	local count=0
	local iname
	local lastiname=""
	local lastitype=""

	#echo "p1=$1"

	#parae input files
	atom_parse_type $1

	#echo "N=$N, T=$T"

	#find what?
	if [ "$N" == "" ]; then
		iname="*.xgb"
	else
		iname="*$N*.xgb"
	fi

	#type?
	if [ -n "$T" ]; then
		results=$(find $XGPATH_LIB -iname "$iname" | grep "/$T/")
	else
		results=$(find $XGPATH_LIB -iname "$iname")
	fi

	for pack in $results
	do
		atom_parse_path $pack

		#check same with the last one?
		if [ "$T" == "$lastitype" -a "$N" == "$lastiname" ]; then
			#skip.
			continue
		fi

		count=$(($count + 1))
		lastitype=$T
		lastiname=$N
		gpkg_searchOK $T $N $count
	done
}


#
# download packet
#

#check and download package, $1 is URL, $2 is local file name.
check_and_download_raw()
{
	local fname=$2
	local retry

	#retry 3 times
	for((retry=0; retry<3; retry++))
	do
		#check if package has already exist.
		if [ -f ${XGPATH_SOURCE}/${fname} ]; then
			printf "%s exists, Skip.\n" $fname
			return 0
		else
			printf "downloading from %s, retry %d...\n" ${1} ${retry}

			[ -f ${XGPATH_SOURCE}/${fname}.tmp ] && \
				rm -f ${XGPATH_SOURCE}/${fname}.tmp 

			wget $1 -O ${XGPATH_SOURCE}/${fname}.tmp
			if [ "$?" == "0" ]; then
				#download ok.
				mv ${XGPATH_SOURCE}/${fname}.tmp ${XGPATH_SOURCE}/${fname}
				return 0
			fi
			sleep 2
		fi
	done

	printf "down load failed, quit.\n"
	return 1
}

#check and download package, $1 is URL.
check_and_download()
{
	local fname=${1##*/}

	#call raw mode.
	check_and_download_raw $1 $fname
}


# return specifid xgb files in $newest, with full path
# $1 is file atom with type, such as "sys-app/vim-7.2.404"
gpkg_getxgbfile()
{
	local results
	local pack
	local count=0
	local iname

	#echo "p1=$1"

	#parae input files
	atom_parse_type $1

	#echo "N=$N, T=$T"

	#find what?
	if [ "$N" == "" ]; then
		echo "No package specified."
		echo "Sample: "
		echo -e "\tgpkg -i vim"
		echo -e "\tgpkg -i vim-7.2.404"
		echo -e "\tgpkg -i app-editors/vim-7.2.404"
		return 1
	else
		iname="$N"
	fi

	#type?
	if [ -n "$T" ]; then
		results=$(find $XGPATH_LIB -type d -iname "$iname" | grep "/$T/")
	else
		results=$(find $XGPATH_LIB -type d -iname "$iname")
	fi

	for pack in $results
	do
		#atom_parse_path $pack
		count=$(($count + 1))
		#echo "$count $pack"
	done

	if [ "$count" == "1" ]; then
		#found 1.
		gpkg_getnewest $results
		return 0
	fi

	if [ "$count"0 -gt 10 ]; then
		#more results
		echo "Too more results"
		echo $results
		return 1
	fi

	#no results. search xgbfile
	if [ -n "$T" ]; then
		results=$(find $XGPATH_LIB  -iname "$N.xgb" | grep "/$T/")
	else
		results=$(find $XGPATH_LIB  -iname "$N.xgb")
	fi

	count=0
	for pack in $results
	do
		#atom_parse_path $pack
		count=$(($count + 1))
		#echo "$count $pack"
	done

	if [ "$count" == "1" ]; then
		#found 1.
		newest=$results
		return 0
	fi

	if [ "$count"0 -gt 10 ]; then
		#more results
		echo "Too more results"
		echo $results
		return 1
	fi

	echo "Not found."
	return 2
}

#begin download, $1 is atom with path,  such as 
#/var/sys-app/autoconf/autoconf-2.63-r1.xgb
gpkg_begin_download()
{
	local url

	#echo "begin download from $XGPATH_SCRIPT/$1"

	#find newest version.
	atom_parse_path $1
	showinfo ">> Begin download $N-$V$R..."

	#clear all routines by include template file
	. $XGPATH_LIB/template.xgb
	err_check "[Error] $XGPATH_LIB/template.xgb not found."

	#prepare XGB_CONFIG, CFLAGS, CPPFLAGS, XGINST_PREFIX
	export CC=$CC
	export CXX=$CXX
	export CFLAGS=$CFLAGS
	export CXXFLAGS=$CXXFLAGS
	XGB_CONFIG="$XGINST_PREFIX "
	XGPATH_SCRIPT=$XGPATH_LIB/$T/$N

	#include package build scripts
	. $1
	err_check "[Error] $XGPATH_SCRIPT/$1 not found."

	#init build script file
	xgb_init

	#create source directory.
	mkdir -p $XGPATH_SOURCE
	err_check "[Error] Create dir $XGPATH_SOURCE failed."

	#download all files
	for url in ${SRC_URI}
	do
		#echo $url
		check_and_download $url
		err_check "[Error] download $url failed."
	done

	#check
	showOK ">> Download OK"
	return 0
}

gpkg_download()
{
	local newest

	#get xgb file
	gpkg_getxgbfile $1

	if [ "$?" == "0" ]; then
		#found, download it
		gpkg_begin_download $newest

	else
		#not found
		return 1
	fi
}


#
# Install package
#

gpkg_rm_dir_safe()
{
	local files

	files=$(ls "$1" 2>/dev/null)
	if [ -z "$files" ]; then
		printf "Remove dir %s\n"  "$1"
		rm -rf "$1"
	fi
}

#strip unnessesary i18n in path $1
gpkg_strip_i18n()
{
	local ipath="$1/locale"
	local i18ns=$(ls $ipath 2>/dev/null)
	local name

	for i in $i18ns
	do
		name=${i##*/}
		str_isIn "$XG_I18N" $name 
		if [ "$?" == "0" ]; then
			#not in XG_I18N, remove.
			showinfo ">> strip i18n $name.."
			rm -rf $ipath/$name
		fi
	done
}

#strip unnessesary i18n in man path.
gpkg_strip_mani18n()
{
	local ipath="$1/man"
	local i18ns=$(ls $ipath 2>/dev/null)
	local name

	for i in $i18ns
	do
		name=${i##*/}

		#skip man1 -- man9
		str_isIn "$name" "man"
		if [ "$?" == "1" ]; then
			continue
		fi

		str_isIn "$XG_I18N" $name 
		if [ "$?" == "0" ]; then
			#not in XG_I18N, remove.
			showinfo ">> strip man-i18n $name.."
			rm -rf $ipath/$name
		fi
	done
}

#strip binary in directory $1
gpkg_stip_bin()
{
	showinfo ">> strip debug info.."
	find $1/{,usr/}{bin,sbin} -type f \
  		-exec strip --strip-all '{}' ';' 2>/dev/null

	find $1/{,usr/}lib -type f \
  		-exec strip --strip-debug '{}' ';' 2>/dev/null
}

#compress man docs
gpkg_gzip_man()
{
	showinfo ">> compress man docs.."
	find $1/usr/{,share/}man -type f \
  		-exec gzip '{}' ';' 2>/dev/null
}

#compress info docs
gpkg_gzip_info()
{
	local ipath=""

	[ -d $1/usr/share/info ] && ipath=$1/usr/share/info
	[ -d $1/usr/info ] && ipath=$1/usr/info

	#quit if not found
	[ -z "$ipath" ] && return 0

	showinfo ">> compress $ipath"

	#first, rename dir.
	[ -f ${ipath}/dir ] && mv ${ipath}/{dir,dir-$N.info}
	find $ipath -type f \
  		-exec gzip '{}' ';' 2>/dev/null
}

gpkg_mkpackage()
{
	local destpath="$XGPATH_DEST/$XGPATH_DB/$T/$N"

	#strip i18n
	gpkg_strip_i18n $XGPATH_DEST/usr/share

	#strip man i18n pages
	gpkg_strip_mani18n $XGPATH_DEST/usr
	gpkg_strip_mani18n $XGPATH_DEST/usr/share

	#strip bin
	gpkg_stip_bin $XGPATH_DEST


	#compress man docs
	gpkg_gzip_man $XGPATH_DEST
	gpkg_gzip_info $XGPATH_DEST

	#generate file list
	cd $XGPATH_DEST
	find | $XGFILEINFO > /tmp/file-$N-$V$R
	err_check "call $XGFILEINFO failed."

	mkdir -p $destpath
	err_check "[Error] create db directory failed."

	#file list
	mv /tmp/file-$N-$V$R $destpath/$N-$V$R.file
	err_check "[Error] move file list failed."

	#configure result and zip it.
	mv /tmp/xiange/$N-$V$R.config $destpath
	gzip $destpath/$N-$V$R.config

	#test result and zip it.
	mv /tmp/xiange/$N-$V$R.test $destpath
	gzip $destpath/$N-$V$R.test

	#copy scripts file
	cp $XGPATH_SCRIPT/$N-$V$R.xgb $destpath/
	err_check "[Error] move file list failed."

	#add configuration parameters
	echo $XGB_CONFIG > $destpath/$N-$V$R.para

	#date time
	date +%F,%T > $destpath/$N-$V$R.date

	#tar it.
	mkdir -p $XGPATH_PACKAGE
	showinfo ">> make packages... please wait a minute"
	tar cjpf $XGPATH_PACKAGE/$N-$V$R.xgp *

}

gpkg_rm_packgeinfo()
{
	local destpath=$XGPATH_SCRIPT

	#remove config file
	echo "remove file $destpath/$N-$V$R.config"
	rm -f $destpath/$N-$V$R.config
	rm -f $destpath/$N-$V$R.config.gz
	rm -f $destpath/$N-$V$R.para

	#remove scripts file
	echo "remove file $destpath/$N-$V$R.xgb"
	rm -f $destpath/$N-$V$R.xgb

	#remove list file
	echo "remove file $destpath/$N-$V$R.file"
	rm -f $destpath/$N-$V$R.file

	#remove test file.
	echo "remove file $destpath/$N-$V$R.test"
	rm -f $destpath/$N-$V$R.test
	rm -f $destpath/$N-$V$R.test.gz

	#remove date file
	echo "remove file $destpath/$N-$V$R.file"
	rm -f $destpath/$N-$V$R.date

	#remove directory
	gpkg_rm_dir_safe $destpath
	gpkg_rm_dir_safe $XGPATH_DB/$T
	gpkg_rm_dir_safe $XGPATH_DB
}

#Check if already insalled. $T/$N$V 
gpkg_checkInst()
{
	[ -f $XGPATH_DB/$T/$N/$N-$V$R.file ] && return 0
	return 1
}

gpkg_begininstall()
{
	local ret=0


	# DO RM WITH CAUTION!! 
	rm -rf /tmp/xiange/sources

	showinfo ">> unpacking $N-$V$R..."
	mkdir -p $XGPATH_UNPACK
	cd $XGPATH_UNPACK
	xgb_unpack
	err_check "[Error] call xgb_unpack failed"

	#call config and store config result
	rm -f /tmp/xiange/$N-$V$R.pipe
	mkfifo /tmp/xiange/$N-$V$R.pipe
	tee /tmp/xiange/$N-$V$R.config < /tmp/xiange/$N-$V$R.pipe & 
	xgb_config >  /tmp/xiange/$N-$V$R.pipe
	if [ "$?" != "0" ]; then
		sleep 1
		echo "[Error] call xgb_config failed"
		rm -f /tmp/xiange/$N-$V$R.pipe
		exit 1
	fi

	#call build
	xgb_build
	err_check "[Error] call xgb_build failed"

	#check if possiable, and ignore result
	#store information to /tmp/xiange/$N-$V$R.test
	rm -f /tmp/xiange/$N-$V$R.pipe2
	mkfifo /tmp/xiange/$N-$V$R.pipe2
	tee /tmp/xiange/$N-$V$R.test < /tmp/xiange/$N-$V$R.pipe2 & 
	xgb_check > /tmp/xiange/$N-$V$R.pipe2

	#call install
	XGPATH_DEST=/tmp/xiange/pack/$N-$V$R
	rm -rf $XGPATH_DEST
	mkdir -p $XGPATH_DEST
	xgb_install
	err_check "[Error] call xgb_install failed"
	
	#make package
	gpkg_mkpackage
	err_check "[Error] make binary package failed"

	#copy to root
	showinfo ">> Moving files to / ..."
	tar xjpf $XGPATH_PACKAGE/$N-$V$R.xgp -C /
	err_check "[Error] copy binary package to / failed"
	showinfo ">> Install OK, cleaning up.."
	
	#post install
	xgb_postinst
	err_check "[Error] call xgb_postinst failed"

	#clean up
	rm -rf /tmp/xiange
	showOK "[***] $N-$V$R installed to your system."
	return 0
}

#install packge. $1 is atom with type, such as "app-editors/vim-7.2.404"
#or only name with version, such as "vim-7.2.404"
#or only name, such as "vim"
gpkg_install()
{
	local newest
	local ret
	local itype
	local iname
	local iver
	local irver

	#parse input 
	atom_parse_type $1
	itype=$T
	iname=$N
	iver=$V
	irver=$R

	#get xgb file
	gpkg_getxgbfile $1
	if [ $? == 0 ]; then

		#check if installed.
		atom_parse_path $newest
		gpkg_checkInst && showOK ">> $N-$V$R has already been installed." && return 0
		#found, download it
		gpkg_begin_download $newest
		ret=$?
		if [ $? == 0 ]; then
			#download ok, install it.
			gpkg_begininstall $newest
		else
			return $ret
		fi

	else
		#not found
		return 1
	fi
}




# $1 is package name, such as "glibc"
# $2 is leading charaters before package name
gpkg_showdep()
{
	local newest
	local ret
	local itype
	local iname
	local iver
	local irver
	local dep
	local leading

	#parse input 
	atom_parse_type $1
	itype=$T
	iname=$N
	iver=$V
	irver=$R

	#get xgb file
	gpkg_getxgbfile $1
	if [ $? == 0 ]; then

		#check if installed.
		atom_parse_path $newest
		gpkg_checkInst && echo "$N-$V$R already installed." && return 0

		#check if in global set.

		#not in global set, add it.

		#found, include and print deps
		#echo ">>> begin show $newest.."

		#include file.
		. $XGPATH_LIB/template.xgb
		err_check "include xgb template file failed."

		#call init
		. $XGPATH_SCRIPT/$T/$N/$N-$V$R.xgb
		err_check "include xgb file failed."

		xgb_init > /dev/null 2>&1
		err_check "call xgb_init failed"

		#show package
		echo "${2} ${1}"

		#check if package installed.
		#show all deps
		#echo ">>> DEP = $DEPEND"

		leading="${2}    "
		for dep in $DEPEND
		do
			gpkg_showdep "$dep" "$leading"	
			err_check "Error: DEP $dep Not found."
		done
		
	else
		#not found
		return 1
	fi
}


#
# remove package
#

#operations when read a file.
# l = list files
# f = remove files/symbol links
# d = remove directorys
XG_CSV_OP="l"

gpkg_csvop_ls()
{
	case "${value[0]}" in
	D)
		#directory
		printf "$NORMAL%s| %8s| $INFO%s\n" "d" "${value[2]}" "${value[1]}"
		;;
	S)
		#Link
		printf "$NORMAL%s| %8s| $BRACKET%s\n" "l" "${value[2]}" "${value[1]}"
		;;
	F)
		#File
		printf "$NORMAL%s| %8s| $FAILURE%s\n" " " "${value[2]}" "${value[1]}"
		;;

	*)
		#totle
		printf "$WARNING\nFile: %s, Dir: %s, Link %d, Size: %dK\n" \
			"${value[1]}"\
			"${value[2]}" "${value[3]}" "${value[5]}"
		;;
	esac
	
}

gpkg_csvop_rm_file()
{
	case "${value[0]}" in
	S)
		#symbol.
		unlink "${value[1]}"
		printf "Remove link %s\n" "${value[1]}"
		;;
	F)
		#file
		unlink "${value[1]}"
		printf "Remove file %s\n" "${value[1]}"
		;;
	esac
	
}



gpkg_mark_remove()
{
	gpkg_mark_remove_array[$mark_count]="$1"
	mark_count=$(($mark_count+1))
}

gpkg_reverse_remove()
{
	local totle=${#gpkg_mark_remove_array[*]}
	local i

	for ((i=$(($totle-1)); i>=0; i--))
	do
		gpkg_rm_dir_safe "${gpkg_mark_remove_array[$i]}"
	done
}

gpkg_csvop_rm_dir()
{
	case "${value[0]}" in
	D)
		#Directory.
		gpkg_mark_remove "${value[1]}"
		;;
	esac
}

# read from stdin
gpkg_read()
{
	local line
	local count
	local -a value

	while IFS= read -r line
	do
		[ -z "$line" ] && continue

		csv_split "$line"
		case "$XG_CSV_OP" in
		l)
			gpkg_csvop_ls
			;;
		f)
			gpkg_csvop_rm_file
			;;
		d)
			gpkg_csvop_rm_dir
			;;
		*)
			gpkg_csvop_ls
			;;
		esac
	done
}

#read from file, and do it one by one
#$1 is total count.
gpkg_do_stage1()
{
	local line
	local count

	while IFS= read -r line
	do
		[ -z "$line" ] && continue
		((count++))
		echo -e "\n*** $count/$1 *** installing $line..\n"
		gpkg -i $line
		[ "$?" != "0" ] && break
	done
}

#read from a file, $1 is file name, $2 is operation
gpkg_readfile()
{
	XG_CSV_OP=$2
	gpkg_read < $1
}

#
# list all installed packages
#

gpkg_list_inst_info()
{
	local files=$(ls $XGPATH_DB/$1/$2/*.xgb)
	local file
	local count
	local -a value
	local t1
	local t2
	local tsize

	for file in $files
	do
		file=${file##*/}
		atom_parse $file

		#get date.
		csv_split `cat $XGPATH_DB/$1/$2/$N-$V$R.date`
		t1=${value[0]}
		t2=${value[1]}

		#get size, 
		csv_split `tail -n 1 $XGPATH_DB/$1/$2/$N-$V$R.file`
		tsize=${value[5]}

		printf "$NORMAL%s %s $INFO%6sK $FAILURE%s\n" $t1 $t2 $tsize $N-$V$R
	done
	
}

gpkg_list_installed()
{
	local gpkg_types
	local type
	local gpkg_packs
	local pack
	local count=0

	gpkg_types=`ls $XGPATH_DB 2>/dev/null`
	for type in $gpkg_types
	do
		gpkg_packs=`ls $XGPATH_DB/$type 2>/dev/null`
		for pack in $gpkg_packs
		do
			gpkg_list_inst_info $type $pack	
		done
	done

}

gpkg_list_showallfile()
{
	local files=$(ls $XGPATH_DB/$1/$2/*.xgb)
	local file
	local count
	local -a value
	local t1
	local t2
	local tsize

	for file in $files
	do
		file=${file##*/}
		atom_parse $file

		gpkg_readfile "$XGPATH_DB/$1/$2/$N-$V$R.file" "l"
	done

}

gpkg_list_package()
{
	local gpkg_types
	local type
	local gpkg_packs
	local pack
	local findcount=0

	gpkg_types=`ls $XGPATH_DB 2>/dev/null`
	for type in $gpkg_types
	do
		gpkg_packs=`ls $XGPATH_DB/$type 2>/dev/null`
		for pack in $gpkg_packs
		do
			if [ "$pack" == "$1" ]; then
				#find, List info.
				gpkg_list_showallfile $type $pack
				findcount=$(($findcount+1))
			fi
		done
	done

	[ "$findcount" == "0" ] && showFailed "package $1 not found"
}

gpkg_do_remove_package()
{
	local files=$(ls $XGPATH_DB/$1/$2/*.xgb)
	local file
	local count
	local -a value
	local t1
	local t2
	local tsize
	local infofile

	for file in $files
	do
		file=${file##*/}
		atom_parse $file
		T=$1
		XGPATH_SCRIPT=$XGPATH_DB/$1/$2

		infofile=$XGPATH_SCRIPT/$N-$V$R.file

		#clear xgb_*
		. $XGPATH_LIB/template.xgb
		err_check "include xgb template file failed."

		#call init
		. $XGPATH_SCRIPT/$N-$V$R.xgb
		err_check "include xgb file failed."

		xgb_init
		err_check "call xgb_init failed"

		#call pre rm
		xgb_prerm
		err_check "call xgb_prerm failed"

		#remove all files
		gpkg_readfile $infofile "f"

		#reset directory list
		unset gpkg_mark_remove_array
		mark_count=0

		#record all directorys
		gpkg_readfile $infofile "d"

		#remove all dirs
		gpkg_reverse_remove

		xgb_postrm
		err_check "call xgb_postinst failed"

		#remove infos.
		gpkg_rm_packgeinfo

	done

	showOK "[***] $N-$V$R removed."


}

gpkg_remove_package()
{
	local gpkg_types
	local type
	local gpkg_packs
	local pack
	local findcount=0

	gpkg_types=`ls $XGPATH_DB 2>/dev/null`
	for type in $gpkg_types
	do
		gpkg_packs=`ls $XGPATH_DB/$type 2>/dev/null`
		for pack in $gpkg_packs
		do
			if [ "$pack" == "$1" ]; then
				#find, remove
				gpkg_do_remove_package $type $pack
				findcount=$(($findcount+1))
			fi
		done
	done

	[ "$findcount" == "0" ] && showFailed "package $1 not found"
}

gpkg_install_binary_package()
{
	local xgbfile
	local atom
	local dbpath

	dbpath=${XGPATH_DB#/}

	showinfo ">> parsing $1..."
	#fine xgbfile in package.
	xgbfile=$(tar tf "$1" | grep "^$dbpath/.*\.xgb")
	err_check "get info from xgb file failed."

	#check xgb
	[ -z "$xgbfile" ] && showFailed "Not xgp format" && exit 1

	xgbfile=${xgbfile#$dbpath}
	xgbfile=${xgbfile#/}
	T=${xgbfile%%/*}
	[ -z "$T" ] && showFailed "Not xgp format" && exit 1

	atom=${xgbfile##*/}
	atom_parse "$atom"

	showinfo ">> installing $T/$N-$V$R ..."

	#copy to root
	showinfo ">> Moving files to / ..."
	tar xjpf "$1" -C /
	err_check "[Error] copy binary package to / failed"
	showinfo ">> Install OK, cleaning up.."

	#clear all routines by include template file
	. $XGPATH_LIB/template.xgb
	err_check "[Error] $XGPATH_LIB/template.xgb not found."

	#prepare XGB_CONFIG
	export CFLAGS=$CFLAGS
	export CPPFLAGS=$CPPFLAGS
	XGB_CONFIG="$XGINST_PREFIX "

	#include package build scripts
	XGPATH_SCRIPT=$XGPATH_DB/$T/$N
	. $XGPATH_SCRIPT/$N-$V$R.xgb
	err_check "[Error] $XGPATH_SCRIPT/$1 not found."

	#init build script file
	xgb_init

	#post install
	xgb_postinst
	err_check "[Error] call xgb_postinst failed"

	showOK "[***] Binary package $N-$V$R installed to you system."
}

gpkg_search_belonging()
{
	local packags
	local i
	local name
	local csv
	local outcnt=0
	local count=0
	local -a value


	packags=$(grep ",$1," `find $XGPATH_DB -iname "*.file"`)
	for i in $packags
	do
		((outcnt++))

		name=${i%%:*}
		name=${name%.file}
		atom_parse_path $name

		csv=${i#*:}
		csv_split $csv	

		echo -e "$FAILURE$T/$N-$V$R : $INFO${value[0]},${value[1]}"
	done

	if [ "$outcnt" == "0" ]; then
		echo ""
		echo -e $FAILURE"Not found. Please try with full path or regular expression, such as"
		echo -e "$INFO\tgpkg -S .*libgcc.*"
		echo -e "$INFO\tgpkg -S /usr/bin/gcc"
		echo ""
		return 1
	fi

	if [ "$outcnt" == "1" ]; then
		return 0
	fi

	return 2
}

gpkg_check_orphan()
{
	local line
	local tcount=0
	local orphan=0
	local conflict=0
	local outcnt=0
	local ret

	while IFS= read -r line
	do
		[ -z "$line" ] && continue
		((tcount++))
		gpkg_search_belonging $line > /dev/null
		ret=$?

		if [ "$ret" == "1" ]; then
			#orphan
			((orphan++))
			((outcnt++))
			printf "$NORMAL%6d %c $INFO%s\n" $outcnt "O" "$line"
		fi

		if [ "$ret" == "2" ]; then
			#orphan
			((conflict++))
			((outcnt++))
			printf "$NORMAL%6d %c $FAILURE%s\n" $outcnt "M" "$line"
		fi
	done

	showOK "Searched $tcount files, $orphan orphans, $conflict conflicts"

}

gpkg_find_orphans()
{
	echo "Searching orphans in $1.."
	find "$1" -type f | gpkg_check_orphan
}

#$1 is file
gpkg_check_rdep()
{
	local i
	local name
	local value

	packags=$(grep ",$1," `find $XGPATH_DB -iname "*.file"`)
	for i in $packags
	do

		name=${i%%:*}
		name=${name%.file}
		atom_parse_path $name
		value="$T/$N-$V$R"

		str_isIn "$sret" "$value"
		if [ "$?" == "0" ]; then
			#not found
			((rcount++))
			sret+="$value "
		fi
	done
}


gpkg_show_rdep()
{
	local bins
	local dlls
	local i
	local dp
	local sret=""
	local count=0
	local rcount=0
	local pcnt=0

	echo "Searching rdep for $1.."	

	bins=$(find /bin /usr/bin /sbin /usr/sbin  -type f 2>/dev/null)
	dlls=$(find /lib /usr/lib -type f -iname "*.so*" 2>/dev/null)
	printf "%-8dFound:%-4d" 0 0
	for i in $bins $dlls
	do
		((count++))
		((pcnt++))
		dp=$(ldd $i 2>/dev/null)
		str_isIn "$dp" "$1"
		if [ "$?" == "1" ]; then
			#fond.
			gpkg_check_rdep "$i"
		fi

		if [ $pcnt -gt 50 ]; then
			printf "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
			printf "%-8dFound:%-4d" $count $rcount
			pcnt=0
		fi

	done

	echo ""
	echo "Result:"
	count=0

	for i in $sret
	do
		((count++))
		printf "%-4d %s\n" $count $i
	done
}

#$1 is package name.
gpkg_rdep()
{
	local sofile
	local i

	sofile=$(gpkg -l $1 | grep "\.so$")
	if [ -z "$sofile" ]; then
		echo "Error: package $1 has no so file"
		exit 1
	fi

	for i in $sofile
	do
		str_isIn "$i" "so"
		if [ "$?" == "0" ]; then
			continue
		fi
		gpkg_show_rdep $(basename $i)
	done

}

#
# parse parameters
#

#reload parameters if available
[ -f /etc/xgparas ] && . /etc/xgparas

case "${1}" in
-v)
	#show version.
	echo $gpkg_version
	;;
-info)
	echo "CC=\"$CC\""
	echo "CXX=\"$CXX\""
	echo "CFLAGS=\"$CFLAGS\""
	echo 'CXXFLAGS="$CFLAGS"'
	echo "XG_I18N=\"$XG_I18N\""
	;;

-s)
	#search package.
	gpkg_search $2
	;;

-S)
	#search file belongs to which package.
	gpkg_search_belonging $2
	;;

-d)
	gpkg_download $2
	#download
	;;

-i)
	gpkg_install $2
	;;

-D)
	#remove package
	gpkg_remove_package $2
	;;

-I)
	#list installed packages, sort by installed time
	gpkg_list_installed $2
	;;

-l)
	#list files in packet.
	gpkg_list_package "$2"
	;;
-ib)
	gpkg_install_binary_package "$2"
	;;

-F)
	gpkg_find_orphans $2
	;;

--sync)
	#sync with git.	
	mkdir -p $XGPATH_LIB
	err_check "[Error] Create dir $XGPATH_LIB failed."

	cd $XGPATH_LIB
	err_check "[Error] enter dir $XGPATH_LIB failed."

	if [ -d .git ]; then
		#alread exist.
		git pull
		err_check "[Error] pull from server failed."
	else
		cd ..
		#remove an system error.
		[ -d /etc ] || mkdir /etc
		[ -f /etc/hosts ] || touch /etc/hosts
		git config --system --add user.name xguser
		git config --system --add user.email xguser@xiangelinux.com
                git clone  $XG_GITSVR
                git clean -xdf
	fi
	;;

selftest)
	#include test file
	. gpkg-test
	err_check "[Error] self test not surpport"
	gpkg_test_bgin
	;;

stage[0-4])
	#read from list, and install it one by one.
	lines=$(wc -l $XGPATH_LIB/stages/$1)
	gpkg_do_stage1 $lines  < $XGPATH_LIB/stages/$1
	;;

-chroot)
	#chroot to new system.

	#check parameter
	if [ -z "$2" ]; then
		echo "Please specify destination, such as:"
		echo -e "\t gpkg chroot /mnt/xg"
		exit 1
	fi

	#check tools directory
#	#if [ -d "$2/tools" ]; then
#		echo "$2 check OK"
#	else 
#		echo "$2/tools not found" 
#		exit 2
#	fi

	#first, check tmpfs

	mkdir -p $2/tmp
	chmod 1777 $2/tmp
	umount $2/tmp 2>/dev/null

	tmpsize=$(df | gawk '$1 == "tmpfs" && $6 == "/dev/shm" {print $4}')
	if [ -n "$tmpsize" ]; then
		if [ "$tmpsize" -ge "3000000" ]; then
			showOK ">> tmpfs size check OK, enabled."
			#mount tmp fs.
			mkdir -p /dev/shm/xgtmp
			chmod 1777 /dev/shm/xgtmp
        		mount --bind /dev/shm/xgtmp $2/tmp
			err_check "Error: mount $2/tmp failed."
		else
			echo -e $WARNING">> tmpfs is too small, disabled"$NORMAL
		fi
	else
		echo -e $WARNING">> no tmpfs on /dev/shm found, disable it"$NORMAL
	fi

	#prepare 2 devie: null and console
	[ -d $2/dev ] || mkdir $2/dev
        [ -c $2/dev/null ] || mknod -m 666 $2/dev/null c 1 3
        [ -c $2/dev/console ] || mknod -m 600 $2/dev/console c 5 1

	#mount proc/sysfs/devfs
	mkdir -p $2/proc
        mkdir -p $2/sys
        mkdir -p $2/dev

	umount $2/proc 2>/dev/null
        umount $2/sys 2>/dev/null
        umount $2/dev 2>/dev/null

	mount -t proc none $2/proc
	err_check "Error: mount $2/proc failed."
        mount -t sysfs none $2/sys
	err_check "Error: mount $2/sys failed."
        mount --bind /dev $2/dev
	err_check "Error: mount $2/dev failed."

	#prepare resolv.conf
	[ -d $2/etc ] || mkdir $2/etc
        [ -f $2/etc/resolv.conf ] || cp /etc/resolv.conf $2/etc


	#chroot
	if [ -f $2/bin/bash ]; then
		bashfile="/bin/bash"
	else
		bashfile="/tools/bin/bash"
	fi

	if [ -f $2/usr/bin/env ]; then
		envfile="/usr/bin/env"
		echo "/usr/bin/env"
	else
		envfile="/tools/bin/env"
		echo "/tools/bin/env"
	fi


	chroot $2 $envfile -i \
		HOME=/root TERM="$TERM" PS1='\u:\w\$ ' \
		PATH=/bin:/usr/bin:/sbin:/usr/sbin:/tools/bin \
		XGSIGN=xg_chroot\
		$bashfile  +h
	err_check "Error: chroot to $2 failed."
	;;

-unchroot)
	#check parameter
	if [ -z "$2" ]; then
		echo "Please specify destination, such as:"
		echo -e "\t gpkg unchroot /mnt/xg"
		exit 1
	fi

	showinfo ">> unmounting $2.."
	umount  $2/tmp 2>/dev/null
	rm -rf /dev/shm/xgtmp 2>/dev/null
	
	umount  $2/proc
	err_check "Error: unmount $2/proc failed."
	umount  $2/sys
	err_check "Error: unmount $2/sys failed."
	umount  $2/dev
	err_check "Error: unmount $2/dev failed."
	showOK "unmount $2 OK"
	;;
-p)
	#show dependece

	#unset DEP,RDEP
	unset GPKG_DEP_LIST

	#show dependant
	gpkg_showdep $2 ""
	;;

-rdep)
	#show reverse dep for specified library
	gpkg_rdep "$2"
	;;
*)
	#show usage 
	gpkg_show_usage
	exit 0
	;;
esac


